<template>
    <div class="apng-player">
        <canvas ref="canvasRef" :width="width" :height="height"></canvas>
    </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch, nextTick } from "vue";
import parseAPNG from "apng-js-plus";

interface Props {
    /** APNG 资源地址（本地 / CDN 均可） */
    src: string;
    /** 画布尺寸，按需调整 */
    width?: number;
    height?: number;
    key?: number;
}
const emit = defineEmits<{
    apngAnimationEnd: [key: number | null];
}>();
const props = withDefaults(defineProps<Props>(), {
    width: 800,
    height: 750,
    key: 1,
});

watch(
    () => props.src,
    () => init(),
    { immediate: false }
);

const canvasRef = ref<HTMLCanvasElement>();
let player: any; // apng-js-plus 的 Player 实例
let ctx: CanvasRenderingContext2D;

/* ---------- 工具：把网络图片转成 ArrayBuffer ---------- */
async function getArrayBuffer(url: string) {
    const blob = await fetch(url).then(r => r.blob());
    return new Promise<ArrayBuffer>(resolve => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result as ArrayBuffer);
        reader.readAsArrayBuffer(blob);
    });
}

const init = async () => {
    if (!props.src) {
        return false;
    }

    const buffer = await getArrayBuffer(props.src);
    const apng = parseAPNG(buffer);
    if (apng instanceof Error) {
        console.error("APNG 解析失败：", apng.message);
        return;
    }
    player = await apng.getPlayer(ctx);
    apng.numPlays = 1; // 只播放一次
    // player.speed = 1;   // 1 倍速（apng-js-plus 支持）
    // 确保只绑定一次
    player.off?.("end"); // 先解绑旧监听（保险）
    // 监听播放结束
    player.on("end", () => {
        emit("apngAnimationEnd", props.key); // number
        nextTick(() => { dispose(); });
    });
    play();
};

/* ---------- 生命周期 ---------- */
onMounted(async () => {
    if (!canvasRef.value) return;
    ctx = canvasRef.value.getContext("2d")!;
    init();
});

onUnmounted(() => {
    dispose();
});

/* ---------- 控制方法 ---------- */
const play = () => player?.play?.();
const pause = () => player?.pause?.();
const reset = () => player?.reset?.();
const dispose = () => {
    player?.dispose?.();
    player = null;
};
</script>

<style scoped>
.apng-player {
    display: inline-block;
}
.ctrl {
    margin-top: 8px;
}
.ctrl button {
    margin-right: 8px;
}
</style>
